﻿using RulesEngine.Actions;
using RulesEngine.Models;
using System.Reflection;

namespace 鸡兔同笼;

public class InvokeRuleAction : EvaluateRuleAction
{
    private readonly RulesEngine.RulesEngine _rulesEngine;
    private readonly MethodInfo _executeActionForRuleResultMethod;
    private readonly MethodInfo _getWorkflowMethod;
    private readonly MethodInfo _compileRuleMethod;

    private object _rulesCache;
    private RuleFunc<RuleResultTree> _cachedRule;

    public InvokeRuleAction(RulesEngine.RulesEngine rulesEngine)
        : base(rulesEngine, null)
    {
        var rulesEngineType = typeof(RulesEngine.RulesEngine);
        var rulesCacheType = rulesEngineType.GetField("_rulesCache", BindingFlags.NonPublic | BindingFlags.Instance).FieldType;
        _getWorkflowMethod = rulesCacheType.GetMethod("GetWorkflow", BindingFlags.Public | BindingFlags.Instance);
        _executeActionForRuleResultMethod = rulesEngineType.GetMethod("ExecuteActionForRuleResult", BindingFlags.NonPublic | BindingFlags.Instance);
        _compileRuleMethod = rulesEngineType.GetMethod("CompileRule", BindingFlags.NonPublic | BindingFlags.Instance, [
            typeof(Rule),
            typeof(RuleExpressionType),
            typeof(RuleParameter[]),
            typeof(Lazy<RuleExpressionParameter[]>)]);
        _rulesEngine = rulesEngine;
    }

    public override async ValueTask<object> Run(ActionContext context, RuleParameter[] ruleParameters)
    {
        if (_cachedRule == null)
        {
            _rulesCache ??= typeof(RulesEngine.RulesEngine)
                .GetField("_rulesCache", BindingFlags.NonPublic | BindingFlags.Instance)
                .GetValue(_rulesEngine);
            var workflowName = context.GetContext<string>("workflowName");
            var ruleName = context.GetContext<string>("ruleName");
            var workflow = (Workflow)_getWorkflowMethod.Invoke(_rulesCache, [workflowName]);
            var rule = workflow.Rules.First(r => r.RuleName == ruleName);
            _cachedRule = (RuleFunc<RuleResultTree>)_compileRuleMethod.Invoke(_rulesEngine, [
                rule,
                workflow.RuleExpressionType,
                ruleParameters,
                new Lazy<RuleExpressionParameter[]>(() => [])]);
        }
        var resultTree = _cachedRule(ruleParameters);
        var result = await (ValueTask<ActionRuleResult>)_executeActionForRuleResultMethod.Invoke(_rulesEngine, [resultTree, true]);
        if (resultTree.ChildResults != null && resultTree.ChildResults.Any())
        {
            await (ValueTask<ActionRuleResult>)_executeActionForRuleResultMethod.Invoke(_rulesEngine, [resultTree.ChildResults.Last(), false]);
        }
        return result;
    }
}
